/**
 * @brief 对于一个描述符进行监控事件的管理
 * @details 一个描述符的事件包括可读，可写，错误，连接断开，任意事件
 */

#pragma once

#include <cstdint>
#include <functional>
#include <sys/epoll.h>

#include "eventLoop.hpp"

namespace hdp
{
    class EventLoop;
    class Channel
    {
    private:
        int _fd;           // 文件描述符
        EventLoop *_loop;  // channel对应的事件循环
        uint32_t _events;  // 当前需要监控的事件
        uint32_t _revents; // 当前连接触发的事件
        using EventCallback = std::function<void()>;
        EventCallback _read_callback;  // 可读事件被触发的回调函数
        EventCallback _write_callback; // 可写事件被触发的回调函数
        EventCallback _error_callback; // 错误事件被触发的回调函数
        EventCallback _close_callback; // 连接断开事件被触发的回调函数
        EventCallback _event_callback; // 任意事件被触发的回调函数
    public:
        Channel(EventLoop *loop, int fd) : _fd(fd), _events(0), _revents(0), _loop(loop) {}

        // 获取文件描述符
        int Fd() { return _fd; }
        // 获取想要监控的事件
        uint32_t Events() { return _events; }
        // 设置实际就绪事件
        void SetRevents(const uint32_t &revents) { _revents = revents; }
        // 设置回调函数
        void SetReadCallback(const EventCallback &cb) { _read_callback = cb; }
        void SetWriteCallback(const EventCallback &cb) { _write_callback = cb; }
        void SetErrorCallback(const EventCallback &cb) { _error_callback = cb; }
        void SetCloseCallback(const EventCallback &cb) { _close_callback = cb; }
        void SetEventCallback(const EventCallback &cb) { _event_callback = cb; }

        // 当前是否监控了可读
        bool ReadAble() { return (_events & EPOLLIN); }
        // 当前是否监控了可写
        bool WriteAble() { return (_events & EPOLLOUT); }
        // 启动可读事件
        void EnableRead()
        {
            _events |= EPOLLIN;
            Update();
        }
        // 启动可写事件
        void EnableWrite()
        {
            _events |= EPOLLOUT;
            Update();
        }
        // 关闭读事件监控
        void DisableRead()
        {
            _events &= (~EPOLLIN);
            Update();
        }
        // 关闭写事件监控
        void DisableWrite()
        {
            _events &= (~EPOLLOUT);
            Update();
        }
        // 关闭所有事件监控
        void DisableAll()
        {
            _events = 0;
            Update();
        }

        // 移除监控
        void Remove();
        // 添加监控
        void Update();

        // 事件处理，一旦发生了事件，就调用这个函数，自己触发 什么事件如何处理自己决定
        void HandleEvent()
        {
            // 可读事件就绪
            // EPOLLIN读事件就绪/EPOLLRDHUP客户端连接半关闭(需要读取缓冲区中可能的剩余数据)/EPOLLPRI存在优先带外数据
            if ((_revents & EPOLLIN) || (_revents & EPOLLRDHUP) || (_revents & EPOLLPRI))
            {
                if (_read_callback)
                {
                    _read_callback();
                }
            }
            // 可写事件就绪
            if (_revents & EPOLLOUT)
            {
                if (_write_callback)
                    _write_callback();
            }
            // 错误事件 //一旦出错，就会释放连接，因此要放到前边调用任意回调
            else if (_revents & EPOLLERR)
            {
                if (_error_callback)
                    _error_callback();
            }
            // 连接断开 //一旦出错，就会释放连接，因此要放到前边调用任意回调
            else if (_revents & EPOLLHUP)
            {
                if (_close_callback)
                    _close_callback();
            }
            if (_event_callback)
                _event_callback();
        }
    };
}